package com.sso.config;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.aaa.entity.TabMenu;
import com.aaa.entity.TabRole;
import com.aaa.entity.TabUser;
import com.aaa.util.ConstUtils;
import com.aaa.util.Result;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sso.dao.MultipleLoginAuthenticationProvider;
import com.sso.dao.MyDaoAuthenticationProvider;

import com.sso.feign.UserFeign;
import com.sso.filter.MultipleLoginAuthenticationFilter;
import com.sso.service.CustomService;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {


    @Resource
    private UserFeign userFeign;
    @Resource
    private CustomService customService;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //
        MultipleLoginAuthenticationFilter multipleLoginAuthenticationFilter = new MultipleLoginAuthenticationFilter();
          multipleLoginAuthenticationFilter.setAuthenticationManager(getAuthManager());
          // 成功 handler
          multipleLoginAuthenticationFilter.setAuthenticationSuccessHandler(getSuccess());
          // 失败 handler
        multipleLoginAuthenticationFilter.setAuthenticationFailureHandler(getFail());
        // 定以 filter
        http.authenticationProvider(getMultipleLoginAuthenticationProvider())
                .addFilterBefore(multipleLoginAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

//        //
        http.formLogin()
                .successHandler(getSuccess()).permitAll()
                .failureHandler(getFail())
                .and()
                .exceptionHandling().accessDeniedHandler(getAceessDefinedHandle())
                .and()
                .authorizeRequests().antMatchers("/","/oauth/**","/login").permitAll()
                .anyRequest().authenticated()
                // 允许跨域
                .and().cors()

                .and()
                .csrf().disable();// 关闭csrf的保护; // 除去不需要认证的路径的其它路径都放行;
    }

    /**
     * sso
     * @return
     */
    @Bean
    public AuthenticationSuccessHandler getSuccess() {
        return (request, response, authentication) -> {
            // 生成token
            // oauth
            // 1. 获取用户名 和密码
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String loginType = request.getParameter("loginType");

            // 认证服务器  2. 发出一个post请求  获取生成token
            HttpRequest params = HttpUtil.createPost(ConstUtils.AUTHTOKEN);
            // 设置请求参数
            params.form("username",username);
            params.form("password",password);
            params.form("grant_type", "password");
            params.form("client_id", "admin");
            params.form("client_secret", "123456");
            params.form("loginType", loginType);

            HttpResponse response1 = params.execute(); // 执行
            // code   body
            String result = response1.body();
            System.out.println("body:"+result);
            JSONObject jsonObject = JSONObject.parseObject(result);   //string是要转化的json字符串     JSONObject为fastjson包里面
            String body = jsonObject.getString(ConstUtils.ACCESS_TOKEN);
            String refreshToken = jsonObject.getString(ConstUtils.REFRESH_TOKEN);


            /**
             * 1. 认证成功之后将token以键值对的形式存到redis里面
             * key: token的值
             * val: token的值
             * 123456：123456
             * 刷新的token：
             * key的值: refresh_token的值
             * val的值：刷新的token的值
             * refresh_123456:78787878
             */
            // 用户的信息存起来
            Map<String,String> map = new HashMap<>();
            map.put("username",username);
            map.put("password",password);
            map.put("grant_type", "password");
            map.put("client_id", "admin");
            map.put("client_secret", "123456");
            map.put("loginType", loginType);
            redisTemplate.opsForHash().putAll(ConstUtils.TOKEN_START+body+"_user",map);
            redisTemplate.expire(ConstUtils.TOKEN_START+body+"_user",60*2,TimeUnit.MINUTES);
            redisTemplate.opsForValue().set(ConstUtils.TOKEN_START+body,ConstUtils.TOKEN_START+body,60, TimeUnit.MINUTES);
            redisTemplate.opsForValue().set("refresh_"+ConstUtils.TOKEN_START+body,ConstUtils.TOKEN_START+refreshToken,60*2, TimeUnit.MINUTES);
            // 刷新的token

            // 认证成功之后
            // 对应的角色的信息
            // 对应的资源的信息
            Map map1 = new HashMap();
            TabUser tabUser = userFeign.getTabUserByName(username);
            // 查询角色的信息
            List<TabRole>  roleList = userFeign.getRoleByUid(tabUser.getId());
            List<String> rolenames = roleList.stream().map(r -> r.getRcode()).collect(Collectors.toList());
            List<Integer> rids = roleList.stream().map(r->r.getRid()).collect(Collectors.toList());
            // 菜单的信息
            List<TabMenu> menusByRids = userFeign.getMenusByRids(rids);
            // 所有的菜单的信息
            List<String> collect = menusByRids.stream().map(m -> m.getMcode()).collect(Collectors.toList());
            System.out.println("------菜单的信息"+collect);
            // 角色的信息
//            Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) authentication.getAuthorities();
//            List<String> menus=authorities.stream().map(simpleGrantedAuthority -> simpleGrantedAuthority.getAuthority()).collect(Collectors.toList());
//            System.out.println(menus);

            map1.put("token",ConstUtils.TOKEN_START+body);
            map1.put("menus",collect);
            map1.put("role",rolenames);
            //
            /**
             *
             *
             *
             *
             *
             *
             */
            Result result1= new Result(200,"成功",map1);
            ObjectMapper objectMapper = new ObjectMapper();
            String jsonmessage = objectMapper.writeValueAsString(result1);
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.print(jsonmessage);
            out.flush();
            out.close();
        };

    }

    //
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(getDaoAuthenticationProvider());
        auth.authenticationProvider(getMultipleLoginAuthenticationProvider());
    }


    @Bean
    public AuthenticationManager getAuthManager() throws Exception {
        List<AuthenticationProvider> providers = new ArrayList<>();
        providers.add(getDaoAuthenticationProvider());
        providers.add(getMultipleLoginAuthenticationProvider());
        return new ProviderManager(providers);
    }

    @Bean
    public AccessDeniedHandler getAceessDefinedHandle(){
        return (request, response, accessDeniedException) -> {
            Result result = Result.accessDefined(null);
            printJsonData(result,response);
        };
    }



    public void printJsonData(Result result, HttpServletResponse response) throws IOException {
        // 以json的形式 传递除去
        ObjectMapper objectMapper = new ObjectMapper();
        String s = objectMapper.writeValueAsString(result);
        // 响应到前端
        response.setContentType("application/json;charset=utf8");
        PrintWriter writer = response.getWriter();
        writer.write(s);
        writer.flush();
        writer.close();
    }

    /**
     * 定义一个provider
     */
    public AuthenticationProvider getDaoAuthenticationProvider(){
        //
        MyDaoAuthenticationProvider myDaoAuthenticationProvider = new MyDaoAuthenticationProvider();
        myDaoAuthenticationProvider.setUserDetailsService(customService);
        myDaoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
        return myDaoAuthenticationProvider;
    }

    public AuthenticationProvider getMultipleLoginAuthenticationProvider(){
        //
        MultipleLoginAuthenticationProvider multipleLoginAuthenticationProvider = new MultipleLoginAuthenticationProvider();
        multipleLoginAuthenticationProvider.setCustomService(customService);
        multipleLoginAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);

        return multipleLoginAuthenticationProvider;
    }


    @Bean
    public AuthenticationFailureHandler getFail(){
        return
                (request, response, exception) -> {
                    // 认证成功之后   返回数据
                    // 用户的信息
                    // code msg t
                    Result result = Result.fail(null);
                    printJsonData(result,response);

                };
    }


}
